/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *    VersionRangePackageConstraint.java
 *    Copyright (C) 2015 University of Waikato, Hamilton, New Zealand
 *
 */
package weka.core.packageManagement;

/**
 * A concrete implementation of PackgageConstraint that encapsulates ranged
 * version number constraints. Handles constraints of the form (u.v.w < package
 * < x.y.z) and (package < u.v.w OR package > x.y.z)
 * 
 * @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
 * @version $Revision: 44030 $
 */
public class VersionRangePackageConstraint extends PackageConstraint {

    /** the lower bound */
    protected String m_lowerBound;

    /** the comparison for the lower bound */
    protected VersionPackageConstraint.VersionComparison m_lowerConstraint;

    /** the upper bound */
    protected String m_upperBound;

    /** the comparison for the upper bound */
    protected VersionPackageConstraint.VersionComparison m_upperConstraint;

    /** is false for version1 <= package <= version2 */
    protected boolean m_boundOr;

    /**
     * Constructor
     * 
     * @param p the package to base this constraint on
     */
    public VersionRangePackageConstraint(Package p) {
        setPackage(p);
    }

    /**
     * Set the range bounds and constraints.
     * 
     * @param bound1 the first bound
     * @param comp1  the first comparison
     * @param bound2 the second bound
     * @param comp2  the second comparison
     * @throws Exception if the range constraint is malformed
     */
    public void setRangeConstraint(String bound1, VersionPackageConstraint.VersionComparison comp1, String bound2, VersionPackageConstraint.VersionComparison comp2) throws Exception {

        // ranged constraint doesn't allow =
        if (comp1 == VersionPackageConstraint.VersionComparison.EQUAL || comp2 == VersionPackageConstraint.VersionComparison.EQUAL) {
            throw new Exception("[VersionRangePackageConstraint] malformed version " + "range constraint (= not allowed)!");
        }

        // if both inequalities are in the same direction then a plain
        // VersionPackageConstraint could be used to cover them both
        if (comp1.compatibleWith(comp2)) {
            throw new Exception("[VersionRangePackageConstraint] malformed " + "version range constraint!");
        }

        // determine which type of range this is
        VersionPackageConstraint.VersionComparison boundsComp = VersionPackageConstraint.compare(bound1, bound2);

        if (boundsComp == VersionPackageConstraint.VersionComparison.EQUAL) {
            throw new Exception("[VersionRangePackageConstraint] malformed version" + " range - both bounds are equal!");
        }

        if (comp1 == VersionPackageConstraint.VersionComparison.GREATERTHAN || comp1 == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL) {

            if (boundsComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                m_boundOr = true; // it is an OR with respect to the two inequalities
            }
        } else {
            if (boundsComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                m_boundOr = true; // it is an OR with respect to the two inequalities
            }
        }

        // store bounds in ascending order
        if (boundsComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
            m_lowerBound = bound1;
            m_lowerConstraint = comp1;
            m_upperBound = bound2;
            m_upperConstraint = comp2;
        } else {
            m_lowerBound = bound2;
            m_lowerConstraint = comp2;
            m_upperBound = bound1;
            m_upperConstraint = comp1;
        }
    }

    /**
     * Get the lower bound of this range
     * 
     * @return the lower bound
     */
    public String getLowerBound() {
        return m_lowerBound;
    }

    /**
     * Get the upper bound of this range
     * 
     * @return the upper bound
     */
    public String getUpperBound() {
        return m_upperBound;
    }

    /**
     * Get the lower comparison
     * 
     * @return the lower comparison
     */
    public VersionPackageConstraint.VersionComparison getLowerComparison() {
        return m_lowerConstraint;
    }

    /**
     * Get the upper comparison
     * 
     * @return the upper comparison
     */
    public VersionPackageConstraint.VersionComparison getUpperComparison() {
        return m_upperConstraint;
    }

    /**
     * Returns true if this is a bounded OR type of constraint
     * 
     * @return true if this is a bounded OR type of constraint
     */
    public boolean isBoundOR() {
        return m_boundOr;
    }

    protected static boolean checkConstraint(String toCheck, VersionPackageConstraint.VersionComparison comp1, String bound1, VersionPackageConstraint.VersionComparison comp2, String bound2, boolean boundOr) {

        boolean result1 = VersionPackageConstraint.checkConstraint(toCheck, comp1, bound1);
        boolean result2 = VersionPackageConstraint.checkConstraint(toCheck, comp2, bound2);

        if (boundOr) {
            return (result1 || result2);
        } else {
            return (result1 && result2);
        }
    }

    /**
     * Check the target package against the constraint embodied in this
     * PackageConstraint.
     * 
     * @param target a package to check with respect to the encapsulated package and
     *               the constraint.
     * 
     * @return true if the constraint is met by the target package with respect to
     *         the encapsulated package + constraint.
     * @throws Exception if the constraint can't be checked for some reason.
     */
    @Override
    public boolean checkConstraint(Package target) throws Exception {
        if (m_lowerConstraint == null || m_upperConstraint == null) {
            throw new Exception("[VersionRangePackageConstraint] No constraint has" + " been set!");
        }

        String targetVersion = target.getPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY).toString();

        return checkConstraint(targetVersion, m_lowerConstraint, m_lowerBound, m_upperConstraint, m_upperBound, m_boundOr);
    }

    protected PackageConstraint checkTargetVersionRangePackageConstraint(VersionRangePackageConstraint target) throws Exception {
        // TODO

        String targetLowerBound = target.getLowerBound();
        String targetUpperBound = target.getUpperBound();

        VersionPackageConstraint.VersionComparison targetLowerComp = target.getLowerComparison();
        VersionPackageConstraint.VersionComparison targetUpperComp = target.getUpperComparison();

        if (!m_boundOr) {
            if (target.isBoundOR()) {
                // construct two VersionPackageConstraints to represent the two
                // target open-ended inequalities
                Package p = (Package) target.getPackage().clone();
                p.setPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY, target.getLowerBound());
                VersionPackageConstraint lowerC = new VersionPackageConstraint(p);
                lowerC.setVersionConstraint(target.getLowerComparison());

                p = (Package) p.clone();
                p.setPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY, target.getUpperBound());
                VersionPackageConstraint upperC = new VersionPackageConstraint(p);
                upperC.setVersionConstraint(target.getUpperComparison());

                PackageConstraint coveringLower = checkTargetVersionPackageConstraint(lowerC);
                if (coveringLower != null) {
                    // return this one. It is possible that both could be covered, but one
                    // is
                    // sufficient (and we have no mechanism for handling two fully bounded
                    // ranges!
                    return coveringLower;
                }

                PackageConstraint coveringUpper = checkTargetVersionPackageConstraint(upperC);
                return coveringUpper; // if we can't cover target at all then this will
                                      // be null
            } else {
                // us and target are both bounded AND
                String resultLowerBound = null;
                String resultUpperBound = null;
                VersionPackageConstraint.VersionComparison resultLowerComp = null;
                VersionPackageConstraint.VersionComparison resultUpperComp = null;

                VersionPackageConstraint.VersionComparison lowerComp = VersionPackageConstraint.compare(m_lowerBound, targetLowerBound);
                if (lowerComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                    resultLowerBound = m_lowerBound;
                    resultLowerComp = VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL;
                    // select the most restrictive inequality
                    if (targetLowerComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || m_lowerConstraint == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        resultLowerComp = VersionPackageConstraint.VersionComparison.GREATERTHAN;
                    }
                } else if (lowerComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                    resultLowerBound = m_lowerBound;
                    resultLowerComp = m_lowerConstraint;
                } else {
                    resultLowerBound = targetLowerBound;
                    resultLowerComp = targetLowerComp;
                }

                VersionPackageConstraint.VersionComparison upperComp = VersionPackageConstraint.compare(m_upperBound, targetUpperBound);
                if (upperComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                    resultUpperBound = m_upperBound;
                    resultUpperComp = VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL;
                    // select the most restrictive inequality
                    if (targetUpperComp == VersionPackageConstraint.VersionComparison.LESSTHAN || m_upperConstraint == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                        resultUpperComp = VersionPackageConstraint.VersionComparison.LESSTHAN;
                    }
                } else if (upperComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                    resultUpperBound = m_upperBound;
                    resultUpperComp = m_upperConstraint;
                } else {
                    resultUpperBound = targetUpperBound;
                    resultUpperComp = targetUpperComp;
                }

                // now check for incompatibility - if resultUpper is less than result
                // lower
                // then the ranges are disjoint
                VersionPackageConstraint.VersionComparison disjointCheck = VersionPackageConstraint.compare(resultUpperBound, resultLowerBound);
                if (disjointCheck == VersionPackageConstraint.VersionComparison.LESSTHAN || disjointCheck == VersionPackageConstraint.VersionComparison.EQUAL) {
                    // TODO if EQUAL then could actually return a VersionPackageConstraint
                    // with an EQUAL constraint.
                    return null;
                }

                // otherwise, we're good to go...
                VersionRangePackageConstraint result = new VersionRangePackageConstraint(getPackage());
                result.setRangeConstraint(resultLowerBound, resultLowerComp, resultUpperBound, resultUpperComp);

                return result;
            }
        } else {
            // we are bounded OR
            if (!target.isBoundOR()) {
                // construct two VersionPackageConstraints to represent our two
                // open-ended inequalities
                Package p = (Package) getPackage().clone();
                p.setPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY, m_lowerBound);
                VersionPackageConstraint lowerC = new VersionPackageConstraint(p);
                lowerC.setVersionConstraint(m_lowerConstraint);

                p = (Package) p.clone();
                p.setPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY, m_upperBound);
                VersionPackageConstraint upperC = new VersionPackageConstraint(p);
                upperC.setVersionConstraint(m_upperConstraint);

                PackageConstraint coveringLower = target.checkTargetVersionPackageConstraint(lowerC);
                if (coveringLower != null) {
                    // return this one. It is possible that both could be covered, but one
                    // is
                    // sufficient (and we have no mechanism for handling two fully bounded
                    // ranges!
                    return coveringLower;
                }

                PackageConstraint coveringUpper = checkTargetVersionPackageConstraint(upperC);
                return coveringUpper; // if the target can't cover us then this will be
                                      // null
            } else {
                // both us and target are bounded ORs. Just need the greatest upper
                // bound
                // and the smallest lower bound of the two
                String resultLowerBound = null;
                String resultUpperBound = null;
                VersionPackageConstraint.VersionComparison resultLowerComp = null;
                VersionPackageConstraint.VersionComparison resultUpperComp = null;

                VersionPackageConstraint.VersionComparison lowerComp = VersionPackageConstraint.compare(m_lowerBound, targetLowerBound);
                if (lowerComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                    resultLowerBound = m_lowerBound;
                    resultLowerComp = VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL;
                    // select the most restrictive inequality
                    if (targetLowerComp == VersionPackageConstraint.VersionComparison.LESSTHAN || m_lowerConstraint == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                        resultLowerComp = VersionPackageConstraint.VersionComparison.LESSTHAN;
                    }
                } else if (lowerComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                    resultLowerBound = m_lowerBound;
                    resultLowerComp = m_lowerConstraint;
                } else {
                    resultLowerBound = targetLowerBound;
                    resultLowerComp = targetLowerComp;
                }

                VersionPackageConstraint.VersionComparison upperComp = VersionPackageConstraint.compare(m_upperBound, targetUpperBound);
                if (upperComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                    resultUpperBound = m_upperBound;
                    resultUpperComp = VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL;
                    // select the most restrictive inequality
                    if (targetUpperComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || m_upperConstraint == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        resultUpperComp = VersionPackageConstraint.VersionComparison.GREATERTHAN;
                    }
                } else if (upperComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                    resultUpperBound = m_upperBound;
                    resultUpperComp = m_upperConstraint;
                } else {
                    resultUpperBound = targetUpperBound;
                    resultUpperComp = targetUpperComp;
                }

                VersionRangePackageConstraint result = new VersionRangePackageConstraint(getPackage());
                result.setRangeConstraint(resultLowerBound, resultLowerComp, resultUpperBound, resultUpperComp);

                return result;
            }
        }
    }

    protected PackageConstraint checkTargetVersionPackageConstraint(VersionPackageConstraint target) throws Exception {

        VersionPackageConstraint.VersionComparison targetComp = target.getVersionComparison();

        String targetVersion = target.getPackage().getPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY).toString();

        VersionPackageConstraint.VersionComparison lowerComp = VersionPackageConstraint.compare(targetVersion, m_lowerBound);
        VersionPackageConstraint.VersionComparison upperComp = VersionPackageConstraint.compare(targetVersion, m_upperBound);

        boolean lowerCheck = false;
        boolean upperCheck = false;
        String coveringLowerBound = null;
        String coveringUpperBound = null;
        VersionPackageConstraint.VersionComparison coveringLowerConstraint = null;
        VersionPackageConstraint.VersionComparison coveringUpperConstraint = null;

        // equals is easy
        if (targetComp == VersionPackageConstraint.VersionComparison.EQUAL) {

            // If our range contains the target version number then the target
            // is the least restrictive constraint that covers both
            if (checkConstraint(target.getPackage())) {
                return target;
            } else {
                return null; // incompatible
            }
        } else {
            if (m_boundOr) {
                // Check against our lower bound (our lower bound is a < or <=)
                // --------------
                // lower bound < case
                if (m_lowerConstraint == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                    if (lowerComp == VersionPackageConstraint.VersionComparison.EQUAL || lowerComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL) {
                            // not compatible with our lower bound
                            lowerCheck = false;
                        } else {
                            lowerCheck = true;
                            // adjust the bounds
                            coveringLowerBound = m_lowerBound;
                            coveringLowerConstraint = m_lowerConstraint;
                        }
                    } else if (lowerComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                        lowerCheck = true;
                        coveringLowerBound = targetVersion;
                        coveringLowerConstraint = targetComp;
                        if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL) {
                            coveringUpperBound = null; // same direction as our lower
                                                       // constraint (so no upper bound)
                        } else {
                            // target's comparison is > or >= (new upper bound = our lower
                            // bound)
                            coveringUpperBound = m_lowerBound;
                            coveringUpperConstraint = m_lowerConstraint;
                        }
                    }
                } else {
                    // lower bound <= case
                    if (lowerComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL) {
                            // not compatible with our lower bound
                            lowerCheck = false;
                        } else {
                            lowerCheck = true;
                            // adjust bounds
                            coveringLowerBound = m_lowerBound;
                            coveringLowerConstraint = m_lowerConstraint;
                        }
                    } else if (lowerComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                        // if the target version is equal to our lower bound then the target
                        // constraint
                        // can't be >
                        if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                            lowerCheck = false;
                        } else {
                            // target constraint is < or <=
                            lowerCheck = true;
                            coveringLowerBound = targetVersion;
                            coveringLowerConstraint = targetComp;

                            coveringUpperBound = null; // same direction as our lower bound
                                                       // (so no upper bound)
                        }
                    } else {
                        // target version is < or = to our lower bound
                        lowerCheck = true;
                        coveringLowerBound = targetVersion;
                        coveringLowerConstraint = targetComp;
                        if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL) {
                            coveringUpperBound = null; // same direction as our lower
                                                       // constraint (so no upper bound)
                        } else {
                            // target's comparison is > or >= (new upper bound = our lower
                            // bound)
                            coveringUpperBound = m_lowerBound;
                            coveringUpperConstraint = m_lowerConstraint;
                        }
                    }
                }
                // end check against our lower bound ----------------

                // check against our upper bound (if necessary)
                if (!lowerCheck) {
                    // Check against our upper bound (our upper bound is a > or >=)
                    // --------------
                    // upper bound > case
                    if (m_upperConstraint == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        if (upperComp == VersionPackageConstraint.VersionComparison.EQUAL || upperComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                            if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL) {
                                // not compatible with our upper bound
                                upperCheck = false;
                            } else {
                                lowerCheck = true;
                                // adjust the bounds
                                coveringUpperBound = m_upperBound;
                                coveringUpperConstraint = m_upperConstraint;
                            }
                        } else if (upperComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                            upperCheck = true;
                            coveringUpperBound = targetVersion;
                            coveringUpperConstraint = targetComp;
                            if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL) {
                                coveringLowerBound = null; // same direction as our upper
                                                           // constraint (so no lower bound)
                            } else {
                                // target's comparison is < or <= (new lower bound = our upper
                                // bound)
                                coveringLowerBound = m_upperBound;
                                coveringLowerConstraint = m_upperConstraint;
                            }
                        }
                    } else {
                        // upper bound >= case
                        if (upperComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                            if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL) {
                                // not compatible with our upper bound
                                upperCheck = false;
                            } else {
                                upperCheck = true;
                                // adjust bounds
                                coveringUpperBound = m_upperBound;
                                coveringUpperConstraint = m_upperConstraint;
                            }
                        } else if (upperComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                            // if the target version is equal to our upper bound then the
                            // target constraint
                            // can't be <
                            if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                                upperCheck = false;
                            } else {
                                // target constraint is > or >=
                                upperCheck = true;
                                coveringUpperBound = targetVersion;
                                coveringUpperConstraint = targetComp;

                                coveringLowerBound = null; // same direction as our upper bound
                                                           // (so no lower bound)
                            }
                        } else {
                            // target version is > or = to our upper bound
                            upperCheck = true;
                            coveringUpperBound = targetVersion;
                            coveringUpperConstraint = targetComp;
                            if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL) {
                                coveringLowerBound = null; // same direction as our upper
                                                           // constraint (so no lower bound)
                            } else {
                                // target's comparison is < or <= (new lower bound = our upper
                                // bound)
                                coveringLowerBound = m_upperBound;
                                coveringLowerConstraint = m_upperConstraint;
                            }
                        }
                    }
                }

                // now return the appropriate type of package constraint to cover this
                // and the
                // target

                if (!lowerCheck && !upperCheck) {
                    // this shouldn't be possible
                    throw new Exception("[VersionRangePackageConstraint] This shouldn't be possible!!");
                }

                if (coveringLowerBound != null && coveringUpperBound != null) {
                    VersionRangePackageConstraint result = new VersionRangePackageConstraint(getPackage());
                    result.setRangeConstraint(coveringLowerBound, coveringLowerConstraint, coveringUpperBound, coveringUpperConstraint);
                    return result;
                }

                String newVersionNumber = (coveringLowerBound != null) ? coveringLowerBound : coveringUpperBound;
                VersionPackageConstraint.VersionComparison newConstraint = (coveringLowerConstraint != null) ? coveringLowerConstraint : coveringUpperConstraint;
                Package p = (Package) getPackage().clone();

                p.setPackageMetaDataElement(VersionPackageConstraint.VERSION_KEY, newVersionNumber);
                VersionPackageConstraint result = new VersionPackageConstraint(p);
                result.setVersionConstraint(newConstraint);

                return result;
            } // end bounded OR case //////////
            else {
                // bounded AND case
                if (lowerComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                    if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL || targetComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                        // outside of our range and inequality is in the wrong direction
                        lowerCheck = false;
                    } else {
                        // outside our range, but inequality in right direction - our range
                        // satisfies both
                        lowerCheck = true;
                        coveringLowerBound = m_lowerBound;
                        coveringLowerConstraint = m_lowerConstraint;
                        coveringUpperBound = m_upperBound;
                        coveringUpperConstraint = m_upperConstraint;
                    }
                } else if (lowerComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                    // satisfiable if target comp is > or >=
                    if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        lowerCheck = true;
                        coveringLowerBound = m_lowerBound;
                        coveringLowerConstraint = (m_lowerConstraint == VersionPackageConstraint.VersionComparison.GREATERTHAN || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) ? VersionPackageConstraint.VersionComparison.GREATERTHAN : VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL;
                        coveringUpperBound = m_upperBound;
                        coveringUpperConstraint = m_upperConstraint;
                    } else {
                        // target comp is < or <= - can satisfy only if <= and our lower
                        // constraint is >=
                        if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL && m_lowerConstraint == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL) {
                            VersionPackageConstraint.VersionComparison newComp = VersionPackageConstraint.VersionComparison.EQUAL;
                            VersionPackageConstraint result = new VersionPackageConstraint(target.getPackage());
                            result.setVersionConstraint(newComp);
                            // we're done
                            return result;
                        }
                    }
                } else if (lowerComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                    // handle the within range (but not on the upper boundary) case here
                    if (upperComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                        if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL) {
                            lowerCheck = true;
                            upperCheck = true;
                            coveringLowerBound = m_lowerBound;
                            coveringLowerConstraint = m_lowerConstraint;
                            coveringUpperBound = targetVersion;
                            coveringUpperConstraint = targetComp;
                        } else {
                            coveringLowerBound = targetVersion;
                            coveringLowerConstraint = targetComp;
                            coveringUpperBound = m_upperBound;
                            coveringUpperConstraint = m_upperConstraint;
                        }
                    }
                }

                if (coveringLowerBound == null || coveringUpperBound == null) {
                    // consider the upper bound
                    if (upperComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                        // satisfiable if target comp is < or <=
                        if (targetComp == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL || targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN) {
                            upperCheck = true;
                            coveringUpperBound = m_upperBound;
                            coveringUpperConstraint = (m_upperConstraint == VersionPackageConstraint.VersionComparison.LESSTHAN || targetComp == VersionPackageConstraint.VersionComparison.LESSTHAN) ? VersionPackageConstraint.VersionComparison.LESSTHAN : VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL;
                            coveringLowerBound = m_lowerBound;
                            coveringLowerConstraint = m_lowerConstraint;
                        } else {
                            // target comp is > or >= - can satisfy only if >= and our upper
                            // constraint is <=
                            if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL && m_upperConstraint == VersionPackageConstraint.VersionComparison.LESSTHANOREQUAL) {
                                VersionPackageConstraint.VersionComparison newComp = VersionPackageConstraint.VersionComparison.EQUAL;
                                VersionPackageConstraint result = new VersionPackageConstraint(target.getPackage());
                                result.setVersionConstraint(newComp);
                                // we're done
                                return result;
                            }
                        }
                    } else if (upperComp == VersionPackageConstraint.VersionComparison.GREATERTHAN) {
                        if (targetComp == VersionPackageConstraint.VersionComparison.GREATERTHAN || targetComp == VersionPackageConstraint.VersionComparison.GREATERTHANOREQUAL || targetComp == VersionPackageConstraint.VersionComparison.EQUAL) {
                            // outside of our range and inequality is in the wrong direction
                            upperCheck = false;
                        } else {
                            // outside our range, but inequality in right direction - our
                            // range satisfies both
                            upperCheck = true;
                            coveringUpperBound = m_upperBound;
                            coveringUpperConstraint = m_upperConstraint;
                            coveringLowerBound = m_lowerBound;
                            coveringLowerConstraint = m_lowerConstraint;
                        }
                    }
                }

                if (coveringUpperBound == null && coveringLowerBound == null) {
                    // we can't satisfy both
                    return null;
                }

                if (coveringUpperBound == null || coveringLowerBound == null) {
                    // this shouldn't happen - either we can't cover it (in which case
                    // both should be null) or we can (in which case both are non-null).
                    throw new Exception("[VersionRangePackageConstraint] This shouldn't be possible!!");
                }

                VersionRangePackageConstraint result = new VersionRangePackageConstraint(getPackage());

                result.setRangeConstraint(coveringLowerBound, coveringLowerConstraint, coveringUpperBound, coveringUpperConstraint);
                return result;
            }
            // check for incompatible first
            /*
             * boolean checkLower = VersionPackageConstraint.checkConstraint(targetVersion,
             * m_lowerConstraint, m_lowerBound);
             */

            /*
             * VersionPackageConstraint lowerBoundCover = new
             * VersionPackageConstraint((Package)(getPackage().clone()));
             * lowerBoundCover.setVersionConstraint(m_lowerConstraint);
             */

        }
    }

    @Override
    public PackageConstraint checkConstraint(PackageConstraint target) throws Exception {

        if (m_lowerConstraint == null || m_upperConstraint == null) {
            throw new Exception("[VersionRangePackageConstraint] No constraint has" + " been set!");
        }

        // have to cover the case where the target is a VersionPackageConstraint or
        // a VersionRangePackageConstraint
        if (!(target instanceof VersionRangePackageConstraint) && !(target instanceof VersionPackageConstraint)) {
            throw new Exception("[VersionRangePackageConstraint] incompatible " + "target constraint!");
        }

        // target is a VersionPackageConstraint
        if (target instanceof VersionPackageConstraint) {
            PackageConstraint result = checkTargetVersionPackageConstraint((VersionPackageConstraint) target);
            return result;
        } else if (target instanceof VersionRangePackageConstraint) {
            // target is VersionRangePackageConstraint
            PackageConstraint result = checkTargetVersionRangePackageConstraint((VersionRangePackageConstraint) target);
            return result;
        }

        return null;
    }

    @Override
    public String toString() {
        String result = m_thePackage.getPackageMetaDataElement("PackageName").toString() + " (" + m_lowerConstraint + m_lowerBound + "|" + m_upperConstraint + m_upperBound + ")";
        return result;
    }
}
